package com.tjbklx1.redis.chap06;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import com.tjbklx1.redis.JedisUtil;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

/**
 * 通讯录自动补全,应对大数据量的场景,
 * @author tjbklx1
 * @date 2018-12-12
 */
public class App62 {

	public static void main(String[] args) {
		System.out.println(Arrays.toString(new App62().findPrefixRange("abc")));
		new App62().run();
	}

	private void run() {
		Jedis conn = JedisUtil.getJedisWithDB();
		testAddressBookAutocomplete(conn);
	}

	public void testAddressBookAutocomplete(Jedis conn) {
		System.out.println("\n----- testAddressBookAutocomplete -----");
		conn.del("members:test");
		System.out.println("the start/end range of 'abc' is: " + Arrays.toString(findPrefixRange("abc")));
		System.out.println();

		System.out.println("Let's add a few people to the guild");
		for (String name : new String[] { "jeff", "jenny", "jack", "jennifer" }) {
			joinGuild(conn, "test", name);
		}
		System.out.println();
		System.out.println("now let's try to find users with names starting with 'je':");
		Set<String> r = autocompleteOnPrefix(conn, "test", "je");
		System.out.println(r);
		assert r.size() == 3;

		System.out.println("jeff just left to join a different guild...");
		leaveGuild(conn, "test", "jeff");
		r = autocompleteOnPrefix(conn, "test", "je");
		System.out.println(r);
		assert r.size() == 2;
		conn.del("members:test");
	}

	private static final String VALID_CHARACTERS = "`abcdefghijklmnopqrstuvwxyz{";

	public String[] findPrefixRange(String prefix) {
		int posn = VALID_CHARACTERS.indexOf(prefix.charAt(prefix.length() - 1));
		char suffix = VALID_CHARACTERS.charAt(posn > 0 ? posn - 1 : 0);
		String start = prefix.substring(0, prefix.length() - 1) + suffix + '{';
		String end = prefix + '{';
		return new String[] { start, end };
	}

	public void joinGuild(Jedis conn, String guild, String user) {
		conn.zadd("members:" + guild, 0, user);
	}

	public void leaveGuild(Jedis conn, String guild, String user) {
		conn.zrem("members:" + guild, user);
	}

	@SuppressWarnings("unchecked")
	public Set<String> autocompleteOnPrefix(Jedis conn, String guild, String prefix) {
		String[] range = findPrefixRange(prefix);
		String start = range[0];
		String end = range[1];
		String identifier = UUID.randomUUID().toString();
		start += identifier;
		end += identifier;
		String zsetName = "members:" + guild;

		conn.zadd(zsetName, 0, start);
		conn.zadd(zsetName, 0, end);

		Set<String> items = null;
		while (true) {
			conn.watch(zsetName);
			int sindex = conn.zrank(zsetName, start).intValue();
			int eindex = conn.zrank(zsetName, end).intValue();
			int erange = Math.min(sindex + 9, eindex - 2);

			Transaction trans = conn.multi();
			trans.zrem(zsetName, start);
			trans.zrem(zsetName, end);
			trans.zrange(zsetName, sindex, erange);
			List<Object> results = trans.exec();
			if (results != null) {
				items = (Set<String>) results.get(results.size() - 1);
				break;
			}
		}

		for (Iterator<String> iterator = items.iterator(); iterator.hasNext();) {
			if (iterator.next().indexOf('{') != -1) {
				iterator.remove();
			}
		}
		return items;
	}
}
